﻿#include "pch.h"
#include "TestAsync.h"

namespace TestAsync
{

class CBase
{
public:
	virtual ~CBase() = default;
	virtual Task<> Func1() = 0;
	virtual Task<int> Func2() = 0;
};

class CDerived1 : public CBase
{
public:
	// Asynchronous function
	Task<> Func1() override
	{
		co_await DelayAsync(1000);
	}

	Task<int> Func2() override
	{
		co_return co_await PostTask([]
		{
			Sleep(1000);
			return 1;
		});
	}
};

class CDerived2 : public CBase
{
public:
	// Synchronous function
	Task<> Func1() override
	{
		Sleep(100);
		return CompletedTask();
	}

	Task<int> Func2() override
	{
		Sleep(100);
		return TaskFromResult(2);
	}
};

Task<> TestSyncAsync()
{
	CBase *p1 = new CDerived1;
	co_await p1->Func1();
	co_await p1->Func2();
	delete p1;

	CBase *p2 = new CDerived2;
	co_await p2->Func1();
	co_await p2->Func2();
	delete p2;
}

class CFoo
{
public:
	CFoo(int val)
	{
		m_ptr = new int(val);
		ATLTRACE("CFoo(): %p\n", m_ptr);
	}

	~CFoo()
	{
		ATLTRACE("~CFoo(): %p\n", m_ptr);
		delete m_ptr;
	}

private:
	int *m_ptr;
};

auto CanceledTask()
{
	auto pState = std::make_shared<AsyncLib::CAssocState<void>>();
	pState->Complete();
	return Task<void>(std::move(pState));
}

Task<> TestCancel2()
{
	CFoo foo(11);
	co_await CanceledTask();
	assert(false);
}

Task<> TestCancel()
{
	TestCancel2();

	CFoo foo(22);
	co_await DelayAsync(60000);
	assert(false);
}

Task<> TestThrow1()
{
	throw std::runtime_error("TestThrow1");
	co_await DelayAsync(1000);
}

Task<> TestThrow2()
{
	co_await DelayAsync(1000);
	throw std::runtime_error("TestThrow2");
}

Task<> TestThrow3()
{
	return PostTask([]
	{
		Sleep(1000);
		throw std::runtime_error("TestThrow3");
	});
}

Task<> TestThrow4()
{
	co_await TestThrow3();
}

Task<> TestException()
{
	int nCount = 0;

	try
	{
		co_await TestThrow1();
	}
	catch (const std::runtime_error& e)
	{
		ATLTRACE("TestException: %s\n", e.what());
		nCount++;
	}

	try
	{
		co_await TestThrow2();
	}
	catch (const std::runtime_error& e)
	{
		ATLTRACE("TestException: %s\n", e.what());
		nCount++;
	}

	try
	{
		co_await TestThrow3();
	}
	catch (const std::runtime_error& e)
	{
		ATLTRACE("TestException: %s\n", e.what());
		nCount++;
	}

	try
	{
		co_await TestThrow4();
	}
	catch (const std::runtime_error& e)
	{
		ATLTRACE("TestException: %s\n", e.what());
		nCount++;
	}

	try
	{
		bool b = TestThrow4().Wait();
	}
	catch (const std::runtime_error& e)
	{
		ATLTRACE("TestException: %s\n", e.what());
		nCount++;
	}

	auto task1 = PostTask([]() -> Task<int>
	{
		throw std::runtime_error("PostTask1");
		co_await DelayAsync(1000);
		co_return 123;
	});

	try
	{
		int val = co_await task1;
	}
	catch (const std::runtime_error& e)
	{
		ATLTRACE("TestException: %s\n", e.what());
		nCount++;
	}

	auto task2 = PostTask([]() -> Task<>
	{
		co_await DelayAsync(1000);
		throw std::runtime_error("PostTask2");
	});

	try
	{
		co_await task2;
	}
	catch (const std::runtime_error& e)
	{
		ATLTRACE("TestException: %s\n", e.what());
		nCount++;
	}

	assert(nCount == 7);

	PostTask([]
	{
		throw std::runtime_error("PostTask3");
	}).Forget();

	PostTask([]() -> Task<>
	{
		co_await DelayAsync(1000);
		throw std::runtime_error("PostTask4");
	}).Forget();
}

} // namespace TestAsync
